/**
 * Copyright (C) 2005-2011 by Rivello Multimedia Consulting (RMC).                    
 * code [at] RivelloMultimediaConsulting [dot] com                                                  
 *                                                                      
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the      
 * "Software"), to deal in the Software without restriction, including  
 * without limitation the rights to use, copy, modify, merge, publish,  
 * distribute, sublicense, and#or sell copies of the Software, and to   
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:                                            
 *                                                                      
 * The above copyright notice and this permission notice shall be       
 * included in all copies or substantial portions of the Software.      
 *                                                                      
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR    
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.                                      
 */
// Marks the right margin of code *******************************************************************
package com.rmc.utils
{
	
	// --------------------------------------
	// Imports
	// --------------------------------------
	import flash.events.Event;
	
	
	// --------------------------------------
	// Metadata
	// --------------------------------------
	
	// --------------------------------------
	// Class
	// --------------------------------------
	/**
	 * <p>This is designed to help easy odds and probability and randomness for game development.</p>
	 * 
	 * <p>AUTHOR  		: Samuel Asher Rivello</p>
	 * <p>COMPANY 		: Rivello Multimedia Consulting</p>
	 * <p>CREATION DATE 	: Jul 14, 2011</p>
	 * 
	 * @example Here is a code example. 
	 *  
	 * <listing version="3.0">
	 * <p>Describe this code example.</p>
	 *
	 * </listing>
	 *
	 */
	public class Random
	{
		
		// --------------------------------------
		// Properties
		// --------------------------------------
		// PUBLIC GETTER/SETTERS
		
		// PUBLIC CONST
		
		// PRIVATE
		
		// PRIVATE STATIC
		/**
		 * Facilitate Singleton.
		 * 
		 */
		private static var _Instance : Random;
		
		// --------------------------------------
		// Constructor
		// --------------------------------------
		/**
		 * This is the constructor.
		 * 
		 */
		public function Random(aSingletonEnforcer : SingletonEnforcer, aSeed_uint: uint = 1)
		{
			//CHECK
			if( aSingletonEnforcer == null ){
				throw new Error("Illegal call to FlashVarsManager constructor; use FlashVarsManager.getInstance() instead");
			}
			
			// VARIABLES
			
			// PROPERTIES
			_seed = _currentSeed = aSeed_uint;
			
			// METHODS
			
		}
		
		
		// --------------------------------------
		// Methods
		// --------------------------------------
		// PUBLIC
		
		// PUBLIC STATIC
		/**
		 * Facilitate Singleton.
		 * 
		 * @return Random
		 * 
		 */
		public static function get instance() : Random 
		{
			if (_Instance == null) { 
				_Instance = new Random(new SingletonEnforcer()); 
			}
			
			return _Instance;
		}
		
		
		/**
		 * Return +1 or -1. Use this to randomly 50/50 multiply by +1 or -1.
		 * 
		 * @param aChance_num : Number (Optional) Used in case you want to influence the result.
		 * 
		 * @return Integer of + 1 or -1. Only.
		 * 
		 */
		public static function getSign(aChance_num : Number = 0.5) : int 
		{
			return (random() < aChance_num) ? 1 : -1;
		}
		
		/**
		 * Return true 50% of the time. 
		 * 
		 * @param aChance_num : Number (Optional) Used in case you want to influence the result.
		 * 
		 * @return Boolean
		 * 
		 */
		public static function getFiftyFifty (aChance_num : Number = 0.5) : Boolean 
		{
			return (Random.getSign() == -1);
		}
		
		
		
		public static function get seed():uint {
			return instance.seed;
		}
		public static function set seed(value:uint):void {
			instance.seed = value;
		}
		
		public static function get currentSeed():uint {
			return instance.currentSeed;
		}
		
		public static function random():Number {
			return instance.random();
		}
		
		public static function float(min:Number,max:Number=NaN):Number {
			return instance.float(min,max);
		}
		
		public static function boolean(chance:Number=0.5):Boolean {
			return instance.boolean(chance);
		}
		
		public static function sign(chance:Number=0.5):int {
			return instance.sign(chance);
		}
		
		public static function bit(chance:Number=0.5):int {
			return instance.bit(chance);
		}
		
		public static function integer(min:Number,max:Number=NaN):int {
			return instance.integer(min,max);
		}
		
		public static function reset():void {
			instance.reset();
		}
		
		
		// constants:
		// private properties:
		protected var _seed:uint=0;
		protected var _currentSeed:uint=0;
		
		// public properties:
		
		// public getter/setters:
		
		// seed = Math.random()*0xFFFFFF; // sets a random seed
		// seed = 50; // sets a static seed
		public function get seed():uint {
			return _seed;
		}
		public function set seed(value:uint):void {
			_seed = _currentSeed = value;
		}
		
		// gets the current seed
		public function get currentSeed():uint {
			return _currentSeed;
		}
		
		// public methods:
		// random(); // returns a number between 0-1 exclusive.
		public function random():Number {
			return (_currentSeed = (_currentSeed * 16807) % 2147483647)/0x7FFFFFFF+0.000000000233;
		}
		
		// float(50); // returns a number between 0-50 exclusive
		// float(20,50); // returns a number between 20-50 exclusive
		public function float(min:Number,max:Number=NaN):Number {
			if (isNaN(max)) { max = min; min=0; }
			return random()*(max-min)+min;
		}
		
		// boolean(); // returns true or false (50% chance of true)
		// boolean(0.8); // returns true or false (80% chance of true)
		public function boolean(chance:Number=0.5):Boolean {
			return (random() < chance);
		}
		
		// sign(); // returns 1 or -1 (50% chance of 1)
		// sign(0.8); // returns 1 or -1 (80% chance of 1)
		public function sign(chance:Number=0.5):int {
			return (random() < chance) ? 1 : -1;
		}
		
		// bit(); // returns 1 or 0 (50% chance of 1)
		// bit(0.8); // returns 1 or 0 (80% chance of 1)
		public function bit(chance:Number=0.5):int {
			return (random() < chance) ? 1 : 0;
		}
		
		// integer(50); // returns an integer between 0-49 inclusive
		// integer(20,50); // returns an integer between 20-49 inclusive
		public function integer(min:Number,max:Number=NaN):int {
			if (isNaN(max)) { max = min; min=0; }
			// Need to use floor instead of bit shift to work properly with negative values:
			return Math.floor(float(min,max));
		}
		
		// reset(); // resets the number series, retaining the same seed
		public function reset():void {
			_seed = _currentSeed;
		}
		
		
		// PRIVATE
		
		// PRIVATE STATIC
		
		
	}
}

// ENFORCE SINGLETON ABOVE
class SingletonEnforcer {}